package com.ningxun.pagentcrawler.service.engine;

import com.ningxun.pagentcrawler.entity.CrawlerConfig;
import com.ningxun.pagentcrawler.entity.policy.PolicyItem;
import com.ningxun.pagentcrawler.util.CrawlerUtil;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;
import com.microsoft.playwright.*;
import com.microsoft.playwright.options.LoadState;

/**
 * 通用爬虫引擎
 * 支持配置化的爬虫功能
 */
@Component
public class UniversalCrawlerEngine {
    
    // 用于去重，防止重复爬取同一政策
    private Set<String> urlSet = ConcurrentHashMap.newKeySet();
    
    // 任务停止检查函数
    private Supplier<Boolean> stopCheckFunction;
    
    /**
     * 设置任务停止检查函数
     */
    public void setStopCheckFunction(Supplier<Boolean> stopCheckFunction) {
        this.stopCheckFunction = stopCheckFunction;
    }
    
    /**
     * 检查是否应该停止任务
     */
    private boolean shouldStop() {
        return stopCheckFunction != null && stopCheckFunction.get();
    }
    
    /**
     * 使用配置执行爬虫
     * 
     * @param config 爬虫配置
     * @return 爬取到的政策列表
     */
    public List<PolicyItem> crawlWithConfig(CrawlerConfig config) {
        List<PolicyItem> policies = new ArrayList<>();
        
        try {
            System.out.println("开始使用配置执行爬虫: " + config.getConfigName());
            
            // 清空URL集合，确保每次都能重新爬取
            urlSet.clear();
            
            // 配置SSL证书验证忽略
            CrawlerUtil.configureSSL();
            
            // 检测是否为Ajax分页
            boolean isAjaxPagination = detectAjaxPagination(config);
            
            if (isAjaxPagination) {
                System.out.println("🔄 检测到Ajax分页，使用单页模式");
                // Ajax分页：只爬取第一页，然后尝试模拟加载更多
                policies = crawlAjaxPagination(config);
            } else {
                System.out.println("📄 使用传统URL分页模式");
                // 传统分页：爬取多页数据
                policies = crawlTraditionalPagination(config);
            }
            
            // 检查是否应该停止
            if (shouldStop()) {
                System.out.println("任务已被取消，停止爬虫执行");
                return policies;
            }
            
            System.out.println("✅ 爬虫完成！总共找到 " + policies.size() + " 条政策数据");
            
        } catch (Exception e) {
            System.err.println("❌ 爬虫执行失败: " + e.getMessage());
            e.printStackTrace();
        }
        
        return policies;
    }
    
    /**
     * 获取页面
     */
    private Document fetchPage(String url, CrawlerConfig config) throws IOException {
        System.out.println("🔍 正在获取页面: " + url);
        try (Playwright playwright = Playwright.create()) {
            Browser browser = playwright.chromium().launch(new BrowserType.LaunchOptions().setHeadless(true));
            BrowserContext context = browser.newContext(new Browser.NewContextOptions()
                .setUserAgent(config.getUserAgent()));
            Page page = context.newPage();
            page.navigate(url);
            // 等待页面网络空闲，确保动态内容加载完成
            page.waitForLoadState(LoadState.NETWORKIDLE);
            String html = page.content();
            Document doc = Jsoup.parse(html, url);
            System.out.println("✅ 页面获取成功");
            System.out.println("🔍 页面标题: " + doc.title());
            System.out.println("🔍 页面大小: " + html.length() + " 字符");
            return doc;
        } catch (Exception e) {
            System.err.println("❌ 页面获取失败: " + e.getMessage());
            throw new IOException("Playwright页面获取失败", e);
        }
    }
    
    /**
     * 构建列表页URL
     */
    private String buildListUrl(CrawlerConfig config, int page) {
        String urlTemplate = config.getListUrlTemplate();
        
        // 第一页特殊处理
        if (page == 1) {
            // 如果URL模板包含{page}，第一页使用基础URL
            if (urlTemplate.contains("{page}")) {
                String baseUrl = config.getBaseUrl();
                if (baseUrl != null && !baseUrl.isEmpty()) {
                    return baseUrl;
                }
            }
        }
        
        // 通用分页逻辑
        if (urlTemplate.contains("{page}")) {
            return urlTemplate.replace("{page}", String.valueOf(page));
        } else {
            // 如果没有{page}占位符，尝试添加分页参数
            String separator = urlTemplate.contains("?") ? "&" : "?";
            return urlTemplate + separator + "page=" + page;
        }
    }
    
    /**
     * 解析列表页
     */
    private List<PolicyItem> parseListPage(Document doc, CrawlerConfig config, int pageNum) {
        List<PolicyItem> policies = new ArrayList<>();
        
        System.out.println("🔍 开始解析列表页，使用选择器: " + config.getListSelector());
        
        // 添加页面调试信息
        System.out.println("🔍 页面标题: " + doc.title());
        System.out.println("🔍 页面URL: " + doc.baseUri());
        
        // 尝试多种选择器
        String[] possibleSelectors = {
            config.getListSelector(),
            "li",
            ".list-item",
            ".item", 
            ".policy-item",
            ".xinxi",
            ".zhengce_list li",
            ".table_list li",
            "ul li",
            ".rotundity_pgContainer li"
        };
        
        Elements items = null;
        String usedSelector = "";
        
        for (String selector : possibleSelectors) {
            if (selector != null && !selector.trim().isEmpty()) {
                items = doc.select(selector);
                System.out.println("🔍 尝试选择器 '" + selector + "': 找到 " + items.size() + " 个元素");
                if (items.size() > 0) {
                    usedSelector = selector;
                    break;
                }
            }
        }
        
        if (items == null || items.size() == 0) {
            System.out.println("❌ 所有选择器都未找到任何元素");
            System.out.println("🔍 页面HTML结构预览:");
            System.out.println(doc.html().substring(0, Math.min(1000, doc.html().length())));
            return policies;
        }
        
        System.out.println("📊 使用选择器 '" + usedSelector + "' 找到 " + items.size() + " 个列表项");
        
        // 显示前几个列表项的HTML内容
        for (int i = 0; i < Math.min(items.size(), 3); i++) {
            Element item = items.get(i);
            System.out.println("🔍 第 " + (i + 1) + " 个列表项HTML: " + item.html().substring(0, Math.min(200, item.html().length())));
        }
        
        for (int i = 0; i < items.size(); i++) {
            Element item = items.get(i);
            System.out.println("🔍 解析第 " + (i + 1) + " 个列表项");
            
            try {
                PolicyItem policy = parsePolicyItem(item, config, pageNum);
                if (policy != null && !urlSet.contains(policy.getUrl())) {
                    urlSet.add(policy.getUrl());
                    policies.add(policy);
                    
                    System.out.println("📄 第 " + pageNum + " 页找到政策: " + policy.getTitle() + 
                                     " (文号: " + policy.getDocumentNumber() + 
                                     ", 内容长度: " + (policy.getContent() != null ? policy.getContent().length() : 0) + " 字符)");
                } else if (policy != null && urlSet.contains(policy.getUrl())) {
                    System.out.println("⚠️ 跳过重复政策: " + policy.getUrl());
                } else {
                    System.out.println("⚠️ 跳过无效政策项目");
                }
            } catch (Exception e) {
                System.err.println("❌ 解析政策项目失败: " + e.getMessage());
            }
        }
        
        return policies;
    }
    
    /**
     * 解析单个政策项目
     */
    private PolicyItem parsePolicyItem(Element item, CrawlerConfig config, int pageNum) {
        // 添加调试信息
        System.out.println("🔍 解析政策项目，使用选择器:");
        System.out.println("   - 列表选择器: " + config.getListSelector());
        System.out.println("   - 标题选择器: " + config.getTitleSelector());
        System.out.println("   - 链接选择器: " + config.getLinkSelector());
        System.out.println("   - 文号选择器: " + config.getDocumentNumberSelector());
        
        // 提取标题（从表格中直接获取）
        String title = "";
        Element titleElement = item.selectFirst(config.getTitleSelector());
        if (titleElement != null) {
            // 添加详细的调试信息
            System.out.println("🔍 标题元素HTML原始内容: " + titleElement.html());
            System.out.println("🔍 标题元素text()方法结果: " + titleElement.text());
            System.out.println("🔍 标题元素text()长度: " + titleElement.text().length());
            
            // 优先使用title属性获取完整标题
            String titleAttr = titleElement.attr("title");
            if (titleAttr != null && !titleAttr.trim().isEmpty()) {
                title = titleAttr.trim();
                System.out.println("✅ 使用title属性获取完整标题: " + title);
                System.out.println("🔍 title属性标题长度: " + title.length());
            } else {
                title = titleElement.text().trim();
                System.out.println("✅ 使用text()方法获取标题: " + title);
                System.out.println("🔍 处理后的标题长度: " + title.length());
            }
        } else {
            System.out.println("❌ 未找到标题元素");
            // 调试：打印当前item的HTML结构
            System.out.println("🔍 当前item的HTML结构: " + item.html());
            // 调试：尝试使用通用选择器
            Element genericTitleElement = item.selectFirst("a");
            if (genericTitleElement != null) {
                title = genericTitleElement.text().trim();
                System.out.println("✅ 使用通用选择器找到标题: " + title);
            } else {
                System.out.println("❌ 使用通用选择器也未找到标题元素");
            }
        }
        
        // 提取链接（只获取表格中的政策详情链接）
        String url = "";
        Element linkElement = item.selectFirst(config.getLinkSelector());
        if (linkElement != null) {
            url = linkElement.absUrl("href");
            System.out.println("✅ 找到链接: " + url);
        } else {
            System.out.println("❌ 未找到链接元素");
            // 调试：尝试使用通用选择器
            Element genericLinkElement = item.selectFirst("a");
            if (genericLinkElement != null) {
                url = genericLinkElement.absUrl("href");
                System.out.println("✅ 使用通用选择器找到链接: " + url);
            } else {
                System.out.println("❌ 使用通用选择器也未找到链接元素");
            }
        }
        
        // 验证URL是否有效
        if (!isValidUrl(url)) {
            System.out.println("⚠️ 跳过无效链接: " + url);
            return null;
        }
        
        // 进一步验证是否为政策详情页链接
        if (!isPolicyDetailUrl(url, config)) {
            System.out.println("⚠️ 跳过非政策链接: " + url);
            return null;
        }
        
        // 提取文号
        String documentNumber = "";
        if (config.getDocumentNumberSelector() != null && !config.getDocumentNumberSelector().isEmpty()) {
            // 首先尝试在当前item中查找文号
            Element docNumberElement = item.selectFirst(config.getDocumentNumberSelector());
            if (docNumberElement != null) {
                documentNumber = docNumberElement.text().trim();
                System.out.println("✅ 在当前项中找到文号: " + documentNumber);
            } else {
                System.out.println("❌ 当前项中未找到文号元素");
                
                // 如果当前item中没有找到，尝试在整个文档中查找对应的文号
                // 通过索引匹配的方式找到对应的文号
                Elements allDocNumberElements = item.ownerDocument().select(config.getDocumentNumberSelector());
                System.out.println("🔍 整个文档中找到 " + allDocNumberElements.size() + " 个文号元素");
                
                // 获取当前item在列表中的索引
                Elements allItems = item.ownerDocument().select(config.getListSelector());
                int currentIndex = -1;
                for (int i = 0; i < allItems.size(); i++) {
                    if (allItems.get(i).equals(item)) {
                        currentIndex = i;
                        break;
                    }
                }
                
                if (currentIndex >= 0 && currentIndex < allDocNumberElements.size()) {
                    documentNumber = allDocNumberElements.get(currentIndex).text().trim();
                    System.out.println("✅ 通过索引匹配找到文号: " + documentNumber + " (索引: " + currentIndex + ")");
                } else {
                    System.out.println("❌ 无法通过索引匹配找到文号 (当前索引: " + currentIndex + ", 文号元素数量: " + allDocNumberElements.size() + ")");
                }
            }
        }
        
        // 提取日期（从表格中直接获取）
        String issueDate = "";
        if (config.getDateSelector() != null && !config.getDateSelector().isEmpty()) {
            Element dateElement = item.selectFirst(config.getDateSelector());
            if (dateElement != null) {
                issueDate = dateElement.text().trim();
                System.out.println("✅ 找到日期: " + issueDate);
            } else {
                System.out.println("❌ 未找到日期元素");
            }
        }
        
        // 爬取详情页内容（只爬取政策详情页）
        String content = "";
        if (!url.isEmpty()) {
            System.out.println("📄 爬取政策详情页: " + title + " -> " + url);
            content = crawlDetailPage(url, config);
        }
        
        // 组合标题和发文字号
        String fullTitle = title;
        if (!documentNumber.isEmpty()) {
            fullTitle = title + " " + documentNumber;  // 改为空格分隔
        }
        
        // 创建政策对象
        PolicyItem policy = new PolicyItem();
        policy.setTitle(fullTitle); // 使用组合后的完整标题
        policy.setUrl(url);
        policy.setContent(content);
        policy.setDocumentNumber(documentNumber);
        policy.setIssueDate(issueDate);
        
        return policy;
    }
    
    /**
     * 爬取详情页内容
     */
    private String crawlDetailPage(String url, CrawlerConfig config) {
        try {
            System.out.println("正在爬取详情页: " + url);
            
            // 配置SSL证书验证忽略
            CrawlerUtil.configureSSL();
            
            Document doc = Jsoup.connect(url)
                    .userAgent(config.getUserAgent())
                    .timeout(config.getTimeout())
                    .get();
            
            // 尝试多个内容选择器
            String content = "";
            String[] contentSelectors = {
                config.getContentSelector(),
                ".content", ".text", ".article-content", ".main-content",
                "#content", "#main", ".detail", ".article",
                "p", ".policy-content", ".document-content"
            };
            
            for (String selector : contentSelectors) {
                if (selector != null && !selector.isEmpty()) {
                    Element contentElement = doc.selectFirst(selector);
                    if (contentElement != null) {
                        content = contentElement.html().trim();
                        if (!content.isEmpty()) {
                            System.out.println("✅ 使用选择器 '" + selector + "' 找到内容");
                            System.out.println("正文预览: " + content.substring(0, Math.min(200, content.length())));
                            return content;
                        }
                    }
                }
            }
            
            // 如果所有选择器都失败，尝试提取页面主要内容
            Element body = doc.body();
            if (body != null) {
                // 移除脚本、样式、导航等元素
                body.select("script, style, nav, header, footer, .nav, .header, .footer").remove();
                content = body.text().trim();
                if (!content.isEmpty()) {
                    System.out.println("⚠️ 使用页面文本作为内容");
                    System.out.println("正文预览: " + content.substring(0, Math.min(200, content.length())));
                    return content;
                }
            }
            
            System.out.println("❌ 未找到内容选择器: " + config.getContentSelector());
            return "";
            
        } catch (Exception e) {
            System.err.println("❌ 爬取详情页失败: " + url + " - " + e.getMessage());
            return "";
        }
    }
    
    /**
     * 检测是否为Ajax分页
     */
    private boolean detectAjaxPagination(CrawlerConfig config) {
        String urlTemplate = config.getListUrlTemplate();
        
        // 特殊处理河北省政府网站
        if (urlTemplate.contains("hebei.gov.cn")) {
            System.out.println("🔍 检测到河北省政府网站URL，使用Ajax分页");
            return true;
        }
        
        // 如果URL包含{page}占位符，说明是传统分页
        if (urlTemplate.contains("{page}")) {
            System.out.println("🔍 URL包含{page}占位符，使用传统分页");
            return false; // 传统分页
        }
        
        // 检查URL是否包含分页参数
        if (urlTemplate.contains("page=") || urlTemplate.contains("p=") || urlTemplate.contains("pageNum=")) {
            System.out.println("🔍 URL包含分页参数，使用传统分页");
            return false; // 传统分页
        }
        
        // 对于河北省政府网站，需要进一步分析
        if (urlTemplate.contains("hebei.gov.cn")) {
            try {
                // 获取页面内容进行分析
                Document doc = fetchPage(urlTemplate, config);
                String html = doc.html().toLowerCase();
                
                // 检测是否有分页相关的JavaScript代码
                String[] ajaxKeywords = {
                    "ajax", "fetch", "xmlhttprequest", "axios",
                    "pagination", "loadmore", "infinite scroll"
                };
                
                for (String keyword : ajaxKeywords) {
                    if (html.contains(keyword)) {
                        System.out.println("🔍 检测到Ajax关键词: " + keyword);
                        return true;
                    }
                }
                
                // 检测是否有分页控件但URL不变
                Elements paginationElements = doc.select(".pagination, .pager, .page-nav, .page-list");
                if (paginationElements.size() > 0) {
                    System.out.println("🔍 检测到分页控件，可能是Ajax分页");
                    return true;
                }
                
                System.out.println("🔍 河北省政府网站未检测到Ajax特征，使用传统分页");
                return false;
                
            } catch (Exception e) {
                System.err.println("❌ Ajax分页检测失败: " + e.getMessage());
                return false;
            }
        }
        
        // 如果URL模板中没有分页占位符且没有分页参数，可能是Ajax分页
        System.out.println("🔍 URL无分页参数，可能是Ajax分页");
        return true; // 可能是Ajax分页
    }
    
    /**
     * 爬取Ajax分页网站
     */
    private List<PolicyItem> crawlAjaxPagination(CrawlerConfig config) {
        List<PolicyItem> policies = new ArrayList<>();
        
        try {
            System.out.println("🔄 开始爬取Ajax分页网站...");
            
            // 智能检测分页模式
            boolean isSinglePageMode = detectSinglePageMode(config);
            
            if (isSinglePageMode) {
                System.out.println("🔍 检测到单页模式，只爬取第一页");
                
                // 只爬取第一页，因为所有内容都在第一页加载
                String listUrl = buildAjaxUrl(config, 1);
                Document doc = fetchPage(listUrl, config);
                
                List<PolicyItem> pagePolicies = parseListPage(doc, config, 1);
                policies.addAll(pagePolicies);
                
                System.out.println("✅ 单页爬取完成，找到 " + pagePolicies.size() + " 条政策数据");
                
            } else {
                System.out.println("🔍 检测到多页模式，尝试爬取多页");
                
                // 对于多页Ajax分页网站，尝试爬取多页
                int maxPages = config.getMaxPages() != null ? config.getMaxPages() : 1000; // 默认1000页
                for (int page = 1; page <= maxPages; page++) {
                    // 检查是否应该停止
                    if (shouldStop()) {
                        System.out.println("任务已被取消，停止爬取第 " + page + " 页");
                        break;
                    }
                    
                    try {
                        System.out.println("正在爬取第 " + page + " 页...");
                        
                        // 构建Ajax请求URL
                        String listUrl = buildAjaxUrl(config, page);
                        Document doc = fetchPage(listUrl, config);
                        
                        List<PolicyItem> pagePolicies = parseListPage(doc, config, page);
                        policies.addAll(pagePolicies);
                        
                        System.out.println("✅ 第 " + page + " 页爬取完成，找到 " + pagePolicies.size() + " 条政策数据");
                        
                        // 如果当前页没有数据，可能已经到最后一页
                        if (pagePolicies.size() == 0) {
                            System.out.println("📄 第 " + page + " 页没有数据，停止爬取");
                            break;
                        }
                        
                        // 请求间隔
                        int requestInterval = config.getRequestInterval() != null ? config.getRequestInterval() : 2000; // 默认2000毫秒
                        if (page < maxPages) {
                            Thread.sleep(requestInterval);
                        }
                        
                    } catch (Exception e) {
                        System.err.println("❌ 爬取第 " + page + " 页失败: " + e.getMessage());
                        continue;
                    }
                }
            }
            
            System.out.println("✅ Ajax分页爬取完成，总共找到 " + policies.size() + " 条政策数据");
            
        } catch (Exception e) {
            System.err.println("❌ Ajax分页爬取失败: " + e.getMessage());
        }
        
        return policies;
    }
    
    /**
     * 构建Ajax请求URL
     */
    private String buildAjaxUrl(CrawlerConfig config, int page) {
        // 使用智能分页URL构建系统
        return buildSmartAjaxUrl(config, page);
    }
    
    /**
     * 检测是否为单页模式（所有内容在一页中，通过JavaScript控制显示）
     */
    private boolean detectSinglePageMode(CrawlerConfig config) {
        try {
            System.out.println("🔍 开始检测分页模式...");
            
            // 检测URL是否包含分页参数模板
            String urlTemplate = config.getListUrlTemplate();
            if (urlTemplate.contains("page=") || urlTemplate.contains("&page=") || urlTemplate.contains("?page=")) {
                System.out.println("🔍 URL包含分页参数模板，使用多页模式");
                return false;
            }
            
            // 对于河北省政府网站，强制使用多页模式
            if (urlTemplate.contains("hebei.gov.cn")) {
                System.out.println("🔍 检测到河北省政府网站URL，使用多页模式");
                return false;
            }
            
            // 获取第一页内容进行分析
            Document doc = fetchPage(config.getListUrlTemplate(), config);
            
            // 检测是否有分页控件
            Elements paginationElements = doc.select(".pagination, .pager, .page-nav, .page-list, .page-num");
            System.out.println("🔍 找到分页控件数量: " + paginationElements.size());
            
            if (paginationElements.size() == 0) {
                System.out.println("🔍 未检测到分页控件，可能是单页模式");
                return true;
            }
            
            // 显示分页控件的详细信息
            for (int i = 0; i < paginationElements.size(); i++) {
                Element pagination = paginationElements.get(i);
                System.out.println("🔍 分页控件 " + (i+1) + " HTML: " + pagination.html().substring(0, Math.min(200, pagination.html().length())));
            }
            
            // 检测分页控件是否只是装饰（没有实际的链接）
            boolean hasActiveLinks = false;
            for (Element pagination : paginationElements) {
                Elements links = pagination.select("a[href]");
                System.out.println("🔍 分页控件中的链接数量: " + links.size());
                
                for (Element link : links) {
                    String href = link.attr("href");
                    String text = link.text().trim();
                    System.out.println("🔍 分页链接: text='" + text + "', href='" + href + "'");
                    
                    if (href != null && !href.isEmpty() && !href.equals("#") && !href.equals("javascript:void(0)")) {
                        hasActiveLinks = true;
                        System.out.println("🔍 找到有效分页链接: " + href);
                        break;
                    }
                }
                if (hasActiveLinks) break;
            }
            
            if (!hasActiveLinks) {
                System.out.println("🔍 分页控件无有效链接，可能是单页模式");
                return true;
            }
            
            // 检测是否有JavaScript分页代码
            String html = doc.html().toLowerCase();
            String[] singlePageKeywords = {
                "loadmore", "infinite scroll", "lazy load", "virtual scroll",
                "动态加载", "懒加载", "无限滚动"
            };
            
            for (String keyword : singlePageKeywords) {
                if (html.contains(keyword)) {
                    System.out.println("🔍 检测到单页模式关键词: " + keyword);
                    return true;
                }
            }
            
            // 检测是否有Ajax相关的JavaScript代码
            String[] ajaxKeywords = {
                "ajax", "fetch", "xmlhttprequest", "axios", "jquery.ajax"
            };
            
            for (String keyword : ajaxKeywords) {
                if (html.contains(keyword)) {
                    System.out.println("🔍 检测到Ajax关键词: " + keyword + "，可能是多页Ajax模式");
                    return false;
                }
            }
            
            System.out.println("🔍 检测到有效分页控件，使用多页模式");
            return false;
            
        } catch (Exception e) {
            System.err.println("❌ 单页模式检测失败: " + e.getMessage());
            // 默认使用多页模式
            return false;
        }
    }
    
    /**
     * 爬取传统分页网站
     */
    private List<PolicyItem> crawlTraditionalPagination(CrawlerConfig config) {
        List<PolicyItem> policies = new ArrayList<>();
        
        // 爬取多页数据
        int maxPages = config.getMaxPages() != null ? config.getMaxPages() : 1000; // 默认1000页
        for (int page = 1; page <= maxPages; page++) {
            // 检查是否应该停止
            if (shouldStop()) {
                System.out.println("任务已被取消，停止爬取第 " + page + " 页");
                break;
            }
            
            try {
                System.out.println("正在爬取第 " + page + " 页...");
                String listUrl = buildListUrl(config, page);
                Document doc = fetchPage(listUrl, config);
                
                List<PolicyItem> pagePolicies = parseListPage(doc, config, page);
                policies.addAll(pagePolicies);
                
                System.out.println("✅ 第 " + page + " 页爬取完成，找到 " + pagePolicies.size() + " 条政策数据");
                
                // 请求间隔
                int requestInterval = config.getRequestInterval() != null ? config.getRequestInterval() : 2000; // 默认2000毫秒
                if (page < maxPages) {
                    Thread.sleep(requestInterval);
                }
                
            } catch (Exception e) {
                System.err.println("❌ 爬取第 " + page + " 页失败: " + e.getMessage());
                continue;
            }
        }
        
        return policies;
    }

    /**
     * 验证URL是否有效
     */
    private boolean isValidUrl(String url) {
        if (url == null || url.isEmpty()) {
            return false;
        }
        
        // 过滤javascript:void(0)
        if (url.equals("javascript:void(0)") || url.startsWith("javascript:")) {
            return false;
        }
        
        // 过滤空链接
        if (url.equals("#") || url.equals("")) {
            return false;
        }
        
        // 确保是HTTP或HTTPS链接
        if (!url.startsWith("http://") && !url.startsWith("https://")) {
            return false;
        }
        
        return true;
    }
    
    /**
     * 判断是否为政策详情页链接
     */
    private boolean isPolicyDetailUrl(String url, CrawlerConfig config) {
        if (url == null || url.isEmpty()) {
            return false;
        }
        
        String lowerUrl = url.toLowerCase();
        
        // 对于政府网站，特殊处理
        if (lowerUrl.contains(".gov.cn")) {
            // 如果URL包含columns和html，说明是政策详情页，应该保留
            if (lowerUrl.contains("columns") && lowerUrl.contains(".html")) {
                System.out.println("✅ 政府网站政策详情页: " + url);
                return true;
            }
            
            // 过滤备案、版权等无关链接，但保留政策相关链接
            String[] excludeKeywords = {
                "conac.cn", "beian", "icp", "备案", "版权", "copyright",
                "guide", "system", "annual", "application", "gazette",
                "指南", "制度", "年报", "申请", "公报"
            };
            
            for (String keyword : excludeKeywords) {
                if (lowerUrl.contains(keyword)) {
                    System.out.println("⚠️ 跳过无关链接: " + url + " (关键词: " + keyword + ")");
                    return false;
                }
            }
        } else {
            // 非政府网站，使用原有的过滤逻辑
            String[] excludeKeywords = {
                "conac.cn", "beian", "icp", "备案", "版权", "copyright",
                "guide", "system", "annual", "application", "gazette", "list", "link",
                "指南", "制度", "年报", "申请", "公报", "清单", "链接"
            };
            
            for (String keyword : excludeKeywords) {
                if (lowerUrl.contains(keyword)) {
                    System.out.println("⚠️ 跳过无关链接: " + url + " (关键词: " + keyword + ")");
                    return false;
                }
            }
        }
        
        // 获取目标网站域名
        String targetDomain = extractDomain(config.getBaseUrl());
        String urlDomain = extractDomain(url);
        
        // 只允许爬取目标网站的链接
        if (!urlDomain.equals(targetDomain)) {
            System.out.println("⚠️ 跳过外部网站链接: " + url + " (目标域名: " + targetDomain + ", 链接域名: " + urlDomain + ")");
            return false;
        }
        
        // 只保留政策详情页链接
        // 政策详情页通常包含这些关键词
        String[] policyKeywords = {
            "detail", "content", "view", "show", "policy", "document",
            "详情", "内容", "查看", "显示", "政策", "文档"
        };
        
        for (String keyword : policyKeywords) {
            if (lowerUrl.contains(keyword)) {
                return true;
            }
        }
        
        // 如果URL包含数字ID，可能是政策详情页
        if (url.matches(".*\\d+.*")) {
            return true;
        }
        
        return true; // 默认认为是政策链接
    }
    
    /**
     * 从URL中提取域名
     */
    private String extractDomain(String url) {
        try {
            if (url == null || url.isEmpty()) {
                return "";
            }
            
            // 移除协议前缀
            String domain = url.replaceAll("^https?://", "");
            
            // 提取域名部分（移除路径）
            int slashIndex = domain.indexOf('/');
            if (slashIndex > 0) {
                domain = domain.substring(0, slashIndex);
            }
            
            // 移除端口号
            int colonIndex = domain.indexOf(':');
            if (colonIndex > 0) {
                domain = domain.substring(0, colonIndex);
            }
            
            return domain.toLowerCase();
        } catch (Exception e) {
            return "";
        }
    }

    /**
     * 分页模式分析结果
     */
    private static class PaginationPattern {
        String type;           // 分页类型：page, offset, start, limit等
        String paramName;      // 参数名
        int currentValue;      // 当前值
        int increment;         // 增量
        String separator;      // URL分隔符
        
        public PaginationPattern(String type, String paramName, int currentValue, int increment, String separator) {
            this.type = type;
            this.paramName = paramName;
            this.currentValue = currentValue;
            this.increment = increment;
            this.separator = separator;
        }
    }
    
    /**
     * 分析URL的分页模式
     */
    private PaginationPattern analyzePaginationPattern(String url) {
        try {
            System.out.println("🔍 开始分析分页模式: " + url);
            
            // 解析URL参数
            String[] urlParts = url.split("\\?", 2);
            String baseUrl = urlParts[0];
            String queryString = urlParts.length > 1 ? urlParts[1] : "";
            
            // 检查常见的分页参数
            String[] paginationParams = {
                "page", "p", "pageNum", "pageNumber", "pg",
                "offset", "start", "from", "skip",
                "limit", "size", "per_page", "count"
            };
            
            for (String param : paginationParams) {
                String pattern = param + "=([0-9]+)";
                java.util.regex.Pattern regex = java.util.regex.Pattern.compile(pattern);
                java.util.regex.Matcher matcher = regex.matcher(queryString);
                
                if (matcher.find()) {
                    int currentValue = Integer.parseInt(matcher.group(1));
                    String separator = queryString.contains("&") ? "&" : "?";
                    
                    // 根据参数类型确定增量
                    int increment = 1;
                    if (param.equals("offset") || param.equals("start") || param.equals("from") || param.equals("skip")) {
                        // 需要先获取页面大小
                        increment = getPageSizeFromUrl(queryString);
                        if (increment == 0) increment = 10; // 默认页面大小
                    }
                    
                    System.out.println("🔍 检测到分页参数: " + param + "=" + currentValue + ", 增量: " + increment);
                    return new PaginationPattern(param, param, currentValue, increment, separator);
                }
            }
            
            // 检查URL路径中的分页模式
            String[] pathPatterns = {
                "/page/([0-9]+)", "/p/([0-9]+)", "/pg/([0-9]+)"
            };
            
            for (String pattern : pathPatterns) {
                java.util.regex.Pattern regex = java.util.regex.Pattern.compile(pattern);
                java.util.regex.Matcher matcher = regex.matcher(url);
                
                if (matcher.find()) {
                    int currentValue = Integer.parseInt(matcher.group(1));
                    System.out.println("🔍 检测到路径分页模式: " + pattern + ", 当前值: " + currentValue);
                    return new PaginationPattern("path", "page", currentValue, 1, "/");
                }
            }
            
            System.out.println("🔍 未检测到分页参数");
            return null;
            
        } catch (Exception e) {
            System.err.println("❌ 分析分页模式时出错: " + e.getMessage());
            return null;
        }
    }
    
    /**
     * 从URL中获取页面大小
     */
    private int getPageSizeFromUrl(String queryString) {
        String[] sizeParams = {"limit", "size", "per_page", "count"};
        
        for (String param : sizeParams) {
            String pattern = param + "=([0-9]+)";
            java.util.regex.Pattern regex = java.util.regex.Pattern.compile(pattern);
            java.util.regex.Matcher matcher = regex.matcher(queryString);
            
            if (matcher.find()) {
                return Integer.parseInt(matcher.group(1));
            }
        }
        
        return 0; // 未找到页面大小参数
    }
    
    /**
     * 根据分页模式构建URL
     */
    private String buildUrlWithPaginationPattern(String originalUrl, PaginationPattern pattern, int targetPage) {
        try {
            String[] urlParts = originalUrl.split("\\?", 2);
            String baseUrl = urlParts[0];
            String queryString = urlParts.length > 1 ? urlParts[1] : "";
            
            if (pattern.type.equals("path")) {
                // 路径分页模式
                String newPath = baseUrl.replaceAll("/page/[0-9]+", "/page/" + targetPage)
                                     .replaceAll("/p/[0-9]+", "/p/" + targetPage)
                                     .replaceAll("/pg/[0-9]+", "/pg/" + targetPage);
                return newPath + (queryString.isEmpty() ? "" : "?" + queryString);
            } else {
                // 参数分页模式
                String newValue;
                if (pattern.paramName.equals("offset") || pattern.paramName.equals("start") || 
                    pattern.paramName.equals("from") || pattern.paramName.equals("skip")) {
                    // 偏移量模式：计算目标偏移量
                    newValue = String.valueOf((targetPage - 1) * pattern.increment);
                } else {
                    // 页码模式：直接使用目标页码
                    newValue = String.valueOf(targetPage);
                }
                
                // 替换或添加分页参数
                String patternStr = pattern.paramName + "=[0-9]+";
                String replacement = pattern.paramName + "=" + newValue;
                
                if (queryString.matches(".*" + patternStr + ".*")) {
                    // 参数已存在，替换它
                    String newQuery = queryString.replaceAll(patternStr, replacement);
                    return baseUrl + "?" + newQuery;
                } else {
                    // 参数不存在，添加它
                    String separator = queryString.isEmpty() ? "?" : "&";
                    return baseUrl + separator + replacement + (queryString.isEmpty() ? "" : "&" + queryString);
                }
            }
            
        } catch (Exception e) {
            System.err.println("❌ 构建分页URL时出错: " + e.getMessage());
            return originalUrl;
        }
    }
    
    /**
     * 智能构建Ajax URL（增强版）
     */
    private String buildSmartAjaxUrl(CrawlerConfig config, int page) {
        String urlTemplate = config.getListUrlTemplate();
        
        System.out.println("🔍 智能构建Ajax URL，模板: " + urlTemplate + ", 页码: " + page);
        
        // 检查URL模板是否包含{page}占位符
        if (urlTemplate.contains("{page}")) {
            String pageUrl = urlTemplate.replace("{page}", String.valueOf(page));
            System.out.println("🔍 替换{page}占位符，第 " + page + " 页URL: " + pageUrl);
            return pageUrl;
        }
        
        // 检查URL模板是否包含{offset}占位符
        if (urlTemplate.contains("{offset}")) {
            // 计算偏移量（假设页面大小为10，如果没有指定的话）
            int pageSize = getPageSizeFromTemplate(urlTemplate);
            int offset = (page - 1) * pageSize;
            String pageUrl = urlTemplate.replace("{offset}", String.valueOf(offset));
            System.out.println("🔍 替换{offset}占位符，第 " + page + " 页URL: " + pageUrl);
            return pageUrl;
        }
        
        // 智能分析分页参数
        PaginationPattern pattern = analyzePaginationPattern(urlTemplate);
        if (pattern != null) {
            String pageUrl = buildUrlWithPaginationPattern(urlTemplate, pattern, page);
            System.out.println("🔍 智能分页模式: " + pattern.type + ", 第 " + page + " 页URL: " + pageUrl);
            return pageUrl;
        }
        
        // 对于河北省政府网站的特殊处理
        if (urlTemplate.contains("hebei.gov.cn")) {
            return buildHebeiGovUrl(urlTemplate, page);
        }
        
        // 通用处理：尝试添加分页参数
        String separator = urlTemplate.contains("?") ? "&" : "?";
        String pageUrl = urlTemplate + separator + "page=" + page;
        System.out.println("🔍 通用Ajax URL，第 " + page + " 页: " + pageUrl);
        return pageUrl;
    }
    
    /**
     * 从URL模板中获取页面大小
     */
    private int getPageSizeFromTemplate(String urlTemplate) {
        String[] sizeParams = {"limit", "size", "per_page", "count"};
        
        for (String param : sizeParams) {
            String pattern = param + "=([0-9]+)";
            java.util.regex.Pattern regex = java.util.regex.Pattern.compile(pattern);
            java.util.regex.Matcher matcher = regex.matcher(urlTemplate);
            
            if (matcher.find()) {
                return Integer.parseInt(matcher.group(1));
            }
        }
        
        return 10; // 默认页面大小
    }
    
    /**
     * 构建河北省政府网站URL
     */
    private String buildHebeiGovUrl(String urlTemplate, int page) {
        // 检查是否已经是API URL格式
        if (urlTemplate.contains("/templates/") && urlTemplate.contains("/blocks/")) {
            // 已经是API URL格式，直接添加分页参数
            String separator = urlTemplate.contains("?") ? "&" : "?";
            String pageUrl = urlTemplate + separator + "page=" + page + "&fix=0";
            System.out.println("🔍 河北省政府网站API URL，第 " + page + " 页: " + pageUrl);
            return pageUrl;
        } else {
            // 需要转换为API URL格式
            String columnsId = extractColumnsId(urlTemplate);
            if (columnsId != null) {
                String apiUrl = "https://www.hebei.gov.cn/columns/" + columnsId + 
                              "/templates/a209d8df-f7f2-4fc9-a7b6-29fa4b059fc4/blocks/f2d5c00c-1653-417e-a178-6ef5a72ca42e?page=" + page + "&fix=0";
                System.out.println("🔍 河北省政府网站转换API URL，第 " + page + " 页: " + apiUrl);
                return apiUrl;
            }
        }
        return urlTemplate;
    }

    /**
     * 从URL中提取栏目ID
     */
    private String extractColumnsId(String url) {
        try {
            if (url == null || url.isEmpty()) {
                return null;
            }
            String lowerUrl = url.toLowerCase();
            if (lowerUrl.contains("/columns/")) {
                String[] parts = url.split("/columns/");
                if (parts.length > 1) {
                    String[] idParts = parts[1].split("/");
                    if (idParts.length > 0) {
                        return idParts[0];
                    }
                }
            }
            return null;
        } catch (Exception e) {
            return null;
        }
    }






} 